package models

import (
	"context"
	"encoding/json"
	"fmt"
	"ginim/utils"
	"github.com/fatih/set"
	"github.com/go-redis/redis/v8"
	"github.com/gorilla/websocket"
	"github.com/spf13/viper"
	"gorm.io/gorm"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
)

type Message struct {
	gorm.Model
	UserId     int64 // sender
	TargetId   int64 // receiver
	Type       int64 // send type: group 2 / broadcast 3 /private 1
	Media      int   // media type: text 1 / emoji 2 / photo 3/ audio 4
	Content    string
	Pic        string
	Url        string
	Desc       string
	Amount     int
	CreateTime uint64 //创建时间
	ReadTime   uint64 //读取时间
}

func (t *Message) TableName() string {
	return "message"
}

type Node struct {
	Conn          *websocket.Conn
	Addr          string
	DataQueue     chan []byte
	GroupSets     set.Interface
	LoginTime     uint64
	HeartbeatTime uint64
	FirstTIme     uint64
}

func (node *Node) Heartbeat(currentTime uint64) {
	node.HeartbeatTime = currentTime
	return
}

// 映射关系
var clientMap = make(map[int64]*Node, 0)

// 读写锁
var rwLocker sync.RWMutex

func Chat(writer http.ResponseWriter, request *http.Request) {
	// check token
	query := request.URL.Query()
	//token := query.Get("token")
	id := query.Get("userId")
	userId, _ := strconv.ParseInt(id, 10, 64)
	//msgType := query.Get("type")
	//targetId := query.Get("targetId")
	//content := query.Get("context")

	isValid := true
	conn, err := (&websocket.Upgrader{
		// token校验
		CheckOrigin: func(r *http.Request) bool {
			return isValid
		},
	}).Upgrade(writer, request, nil)

	if err != nil {
		fmt.Println(err)
		return
	}

	// 获取conn
	currentTime := uint64(time.Now().Unix())
	node := &Node{
		Conn:          conn,
		Addr:          conn.RemoteAddr().String(),
		DataQueue:     make(chan []byte, 50),
		GroupSets:     set.New(set.ThreadSafe),
		HeartbeatTime: currentTime,
		LoginTime:     currentTime,
	}

	// 用户关系
	// userId与node绑定 线程安全
	rwLocker.Lock()
	clientMap[userId] = node
	rwLocker.Unlock()

	// 发送
	go sendProc(node)
	// 接收
	go receiveProc(node)

	key := "online_" + id
	value := []byte(node.Addr)
	ttl := time.Duration(viper.GetInt("timeout.RedisOnlineTime")) * time.Hour
	SetOnlineInfo(key, value, ttl)
	//sendMsg(userId, []byte("欢迎进入聊天系统"))
}

func sendProc(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

func receiveProc(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			fmt.Println(err)
			return
		}

		message := Message{}
		err = json.Unmarshal(data, &message)
		if err != nil {
			fmt.Println(err)
		}

		//if msg.Type == 3 {
		//	currentTime := uint64(time.Now().Unix())
		//	node.Heartbeat(currentTime)
		//	return
		//}

		dispatch(data)
		broadMsg(data)
		fmt.Println("[ws] receiveProc <<<<<<< ", string(data))
	}
}

var udpSendChan = make(chan []byte, 1024)

func broadMsg(data []byte) {
	udpSendChan <- data
}

func init() {
	go udpSendProc()
	go udpReceiveProc()
	fmt.Println("init goroutine")
}

// udp发送消息 协程
func udpSendProc() {
	conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(192, 168, 0, 255),
		Port: 3000,
	})

	defer conn.Close()
	if err != nil {
		fmt.Println(err)
	}

	for {
		select {
		case data := <-udpSendChan:
			fmt.Println("udpSendProc  data :", string(data))
			_, err := conn.Write(data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

// udp接收消息 协程
func udpReceiveProc() {
	conn, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: 3000,
	})

	if err != nil {
		fmt.Println(err)
	}

	defer conn.Close()
	for {
		var buffer [512]byte
		idx, err := conn.Read(buffer[0:])
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("udpReceiveProc data :", string(buffer[0:idx]))
		dispatch(buffer[0:idx])
	}
}

// 后端调度逻辑处理
func dispatch(data []byte) {
	fmt.Println("dispatch data :", string(data))
	message := Message{}
	err := json.Unmarshal(data, &message)
	if err != nil {
		fmt.Println(err)
		return
	}

	switch message.Type {
	case 1:
		sendMsg(message.TargetId, data)
	case 2:
		sendCommunityMsg(message.TargetId, data)
		//case 3:
		//	sendAllMsg()
	}
}

func sendMsg(userId int64, msg []byte) {
	rwLocker.RLock()
	node, ok := clientMap[userId]
	rwLocker.RUnlock()

	message := Message{}
	err := json.Unmarshal(msg, &message)
	if err != nil {
		fmt.Println(err)
	}

	if userId == 0 || message.UserId == 0 {
		return
	}

	ctx := context.Background()
	targetIdStr := strconv.Itoa(int(userId))
	userIdStr := strconv.Itoa(int(message.UserId))

	message.CreateTime = uint64(time.Now().Unix())
	rst, err := utils.REDIS.Get(ctx, "online_"+userIdStr).Result()
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("sendMsg rst", rst, ok)
	if rst != "" {
		if ok {
			fmt.Println("sendMsg >>> userID: ", userId, "  msg:", string(msg))
			node.DataQueue <- msg
		}
	}

	var key string
	if userId > message.UserId {
		key = "msg_" + userIdStr + "_" + targetIdStr
	} else {
		key = "msg_" + targetIdStr + "_" + userIdStr
	}
	res, err := utils.REDIS.ZRevRange(ctx, key, 0, -1).Result()
	if err != nil {
		fmt.Println(err)
	}
	score := float64(cap(res)) + 1
	res1, err := utils.REDIS.ZAdd(ctx, key, &redis.Z{Score: score, Member: msg}).Result() //jsonMsg
	//res, e := utils.Red.Do(ctx, "zadd", key, 1, jsonMsg).Result() //备用 后续拓展 记录完整msg
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(res1)
}

func sendCommunityMsg(targetId int64, msg []byte) {
	ids := FindUserByCommunityId(uint(targetId))
	for i := 0; i < len(ids); i++ {
		if targetId != int64(ids[i]) {
			sendMsg(int64(ids[i]), msg)
		}
	}
}

func RedisMsg(userIdA int64, userIdB int64, start int64, end int64, isRev bool) []string {
	rwLocker.RLock()
	//node, ok := clientMap[userIdA]
	rwLocker.RUnlock()
	//message := Message{}
	//json.Unmarshal(msg, &message)
	ctx := context.Background()
	userIdStr := strconv.Itoa(int(userIdA))
	targetIdStr := strconv.Itoa(int(userIdB))
	var key string
	if userIdA > userIdB {
		key = "msg_" + targetIdStr + "_" + userIdStr
	} else {
		key = "msg_" + userIdStr + "_" + targetIdStr
	}
	//key = "msg_" + userIdStr + "_" + targetIdStr
	//results, err := utils.Red.ZRevRange(ctx, key, 0, 10).Result()  //根据score倒叙

	var results []string
	var err error
	if isRev {
		results, err = utils.REDIS.ZRange(ctx, key, start, end).Result()
	} else {
		results, err = utils.REDIS.ZRevRange(ctx, key, start, end).Result()
	}
	if err != nil {
		fmt.Println(err) //没有找到
	}
	// 发送推送消息
	/**
	// 后台通过websocket 推送消息
	for _, val := range results {
		fmt.Println("sendMsg >>> userID: ", userIdA, "  msg:", val)
		node.DataQueue <- []byte(val)
	}**/
	return results
}
